﻿using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SSN.Security;
using SSN.Objects;
using SSN.Objects.DBPersist;

namespace SSN.Utilities.Database
{
    public abstract class SafeDbObject : SSNDataBasePersistedObject
    {

        #region validation specific code
        private List<FieldLimit> limits = null;

        /// <summary>
        /// Loads the FieldLimit attributes applied to this object.
        /// </summary>
        private void LoadLimits()
        {
            if (limits == null)
            {
                limits = new List<FieldLimit>();
                MemberInfo info = this.GetType();
                PropertyInfo[] properties = this.GetType().GetProperties();

                // Since this is reflection, optimizing the array length to store the length value.
                int l = properties.Length;

                for (int i = 0; i < l; i++)
                {
                    object[] attributes = properties[i].GetCustomAttributes(typeof(FieldLimit), true);
                    int k = attributes.Length;
                    for (int j = 0; j < k; j++)
                    {
                        FieldLimit limit = (FieldLimit)attributes[j];
                        limits.Add(limit);
                        limit.PropertyInfo = properties[i];
                        limit.Object = this;
                    }
                }
            }
        }

        /// <summary>
        /// Initiates a new DBPersisted object.
        /// </summary>
        /// <returns>Whether the initialization of the object was succesful.</returns>
        public override bool InitNew(SSNSecurityContext context)
        {
            LoadLimits();
            return base.InitNew(context);
        }

        /// <summary>
        /// Loads the DBPersisted object from the database.
        /// </summary>
        /// <returns>Whether the load was successful.</returns>
        protected new bool Load(SSNSecurityContext ctx, object[] keys)
        {
            LoadLimits();
            return base.Load(ctx, keys);
        }

        /// <summary>
        /// Validates the length of strings marked with the FieldLimite attribute.
        /// </summary>
        private void Validate()
        {
            if (limits == null)
                return;

            foreach (FieldLimit limit in limits)
            {
                ValidationResult result = limit.IsValid();
                if (!result.Valid)
                {
                    throw new Exception(string.Format("Value '{0}' for property '{1}' exceeds field limit of {2} characters.", result.Value, result.Property, result.Length));
                }
            }
        }

        /// <summary>
        /// Saves the DBPersisted object to the database.
        /// </summary>
        /// <returns>Whether the save was succesful.</returns>
        public override bool Save()
        {
            Validate();
            
            return base.Save();
        }
        #endregion
    }
}
